#include <algorithm>
#include <stdexcept>

#include "smart_vector.h"

smart_vector::smart_vector() : size_(0), capacity_(1)
{
}

smart_vector::smart_vector(long long digit) : size_(1), capacity_(1)
{
   content_.digit_ = digit;
}

smart_vector::smart_vector(const smart_vector& vector) : size_(vector.size_), capacity_(vector.capacity_)
{
   if (capacity_ == 1)
   {
      content_.digit_ = vector.content_.digit_;
   }
   else
   {
      content_.digits_ = new long long[capacity_];
      for (size_t i = 0; i < size_; i++)
      {
         content_.digits_[i] = vector.content_.digits_[i];
      }
   }
}

smart_vector::~smart_vector()
{
   if (capacity_ > 1)
   {
      delete[] content_.digits_;
   }
}

smart_vector& smart_vector::operator=(const smart_vector& vector)
{
   if (this == &vector)
   {
      return *this;
   }
   
   if (capacity_ > 1)
   {
      delete[] content_.digits_;
   }
   
   size_ = vector.size_;
   capacity_ = vector.capacity_;
   if (capacity_ > 1)
   {
      content_.digits_ = new long long[capacity_];
   }
   
	for (size_t i = 0; i < vector.size(); i++)
	{
		(*this)[i] = vector[i];
	}

	return *this;
}

size_t smart_vector::size() const
{
   return size_;
}

void smart_vector::push_back(long long digit)
{
   grow();
   size_++;
   (*this)[size_ - 1] = digit;
}

void smart_vector::pop_back()
{
   if (size_ == 0)
   {
      throw std::runtime_error("No more elements!\n");
   }
   size_--;
   shrink();
}

void smart_vector::swap(smart_vector& vector)
{
   std::swap(content_, vector.content_);
   std::swap(size_, vector.size_);
   std::swap(capacity_, vector.capacity_);
}

void smart_vector::clear()
{
   if (capacity_ > 1)
   {
      delete[] content_.digits_;
      capacity_ = 1;
   }
   size_ = 0;
}

void smart_vector::resize(size_t size, long long digit)
{
   while (capacity_ < size)
   {
      grow(true);
   }
   
   size_t tmp = size_;
   size_ = size;
   
   for (size_t i = tmp; i < size_; i++)
   {
      (*this)[i] = digit;
   }
   
   shrink();
}

void smart_vector::insert(int position, int count, long long digit)
{
   size_t tmp = size_;
   resize(size_ + count, 0);
   for (int i = tmp - 1; i >= position; i--)
   {
      (*this)[i + count] = (*this)[i];
   }
   for (int i = position; i < count; i++)
   {
      (*this)[i] = digit;
   }
}

size_t smart_vector::begin()
{
   return 0;
}

long long& smart_vector::operator[](size_t i)
{
   if (i >= size_)
   {
      throw std::runtime_error("Index out of bounds!");
   }
   
   if (capacity_ == 1)
   {
      return content_.digit_;
   }
   
   return content_.digits_[i];
}

long long smart_vector::operator[](size_t i) const
{
   if (i >= size_)
   {
      throw std::runtime_error("Index out of bounds!");
   }
   
   if (capacity_ == 1)
   {
      return content_.digit_;
   }
   
   return content_.digits_[i];
}

void smart_vector::grow(bool force)
{
   if (!force && size_ < capacity_)
   {
      return;
   }
   
   long long* tmp = new long long[2 * capacity_];
   if (capacity_ == 1)
   {
      tmp[0] = content_.digit_;
   }
   else
   {
      for (size_t i = 0; i < size_; i++)
      {
         tmp[i] = content_.digits_[i];
      }
      delete[] content_.digits_;
   }
   
   content_.digits_ = tmp;
   capacity_ *= 2;
}

void smart_vector::shrink()
{
   if (capacity_ == 1 || size_ * 4 >= capacity_)
   {
      return;
   }
   
   if (capacity_ / 2 == 1)
   {
      long long tmp = content_.digits_[0];
      delete[] content_.digits_;
      content_.digit_ = tmp;
   }
   else
   {
      long long* tmp = new long long[capacity_ / 2];
      for (size_t i = 0; i < size_; i++)
      {
         tmp[i] = content_.digits_[i];
      }
      delete[] content_.digits_;
      content_.digits_ = tmp;
   }
   
   capacity_ /= 2;
}
